import time, os

MONTHS = ['January', 'February', 'March', 'April', 'May', 'June',
    'July', 'August', 'September', 'October', 'November', 'December']
MONTH_ABBRS = dict(zip(['Jan', 'Feb', 'Mar', 'Apr', 'May', 'June', 
                'July', 'Aug', 'Sept', 'Oct', 'Nov', 'Dec'], MONTHS))
DATA_FILE = 'ac_list.txt'
CATCH_FILE = 'catchfile.txt'

# '15,000' => 15000
def int_comma(s): return int(s.replace(',',''))

# individual fish/bug dictionary => readable line of information
def pretty_animal(animal):
    return animal['name']+' ::: '+str(animal['price'])+'b ::: '+animal['loc']

# input: list of strings describing span(s) of months
# output: list of full names of every month in span(s)
# ['Jan','Apr'] => ['January', 'February', 'March', 'April']
def expand_months(month_span):
    full_months = []
    if month_span[0].strip() == 'All year':
        full_months = MONTHS
    elif len(month_span) == 1: # ex. ['month1']
        full_months = MONTH_ABBRS[month_span[0]]
    elif len(month_span) == 2: # ex. ['month1', 'month2']
        a = MONTHS.index(MONTH_ABBRS[month_span[0]])
        b = MONTHS.index(MONTH_ABBRS[month_span[1]])
        if a < b:
            full_months = MONTHS[a:b+1]
        elif a > b: # if it's ['Dec','Feb']
            full_months = MONTHS[:b+1] + MONTHS[a:]
        else: # if it's ['Oct', 'Oct'] because ladybugs are weird
            full_months.append(MONTHS[a])
    elif len(month_span) == 3: # ex. ['month1', 'month2, monthA', 'monthB']
        borders = month_span[1].split(', ')
        span1 = [month_span[0], borders[0]]
        span2 = [borders[1], month_span[2]]
        # recursion!
        full_months = expand_months(span1) + expand_months(span2)

    return full_months

# input: list of strings describing span(s) of hours
# output: list of numbers (0-23) representing hours in the span(s)
# ['9:00am', '4:00pm'] => [9,10,11,12,13,14,15,16]
def expand_hours(hour_span):
    full_hours = []
    if hour_span[0].strip() == 'All day':
        full_hours = range(0, 24)
    if len(hour_span) == 2: # ex: ['hour1', 'hour2']
        a = int(hour_span[0].strip()[0]) # chop the first number off each time
        b = int(hour_span[1].strip()[0]) # ex. '4:00am' => 4
        # make everything 24-hour
        if hour_span[0].endswith('pm'):
            a += 12
        if hour_span[1].endswith('pm'):
            b += 12
        if a < b:
            full_hours = range(a, b+1)
        else:
            full_hours = range(b, 24) + range(0, a+1)
    elif len(hour_span) == 3: # ex: ['hour1', 'hour2, hourA', 'hourB']
        borders = hour_span[1].split(', ')
        span1 = [hour_span[0], borders[0]]
        span2 = [borders[1], hour_span[2]]
        # recursion again!
        full_hours = expand_hours(span1) + expand_hours(span2)

    return full_hours

### EXECUTION START

## read in database file

if not os.path.exists(os.path.join(os.path.split(__file__)[0], DATA_FILE)):
    print DATA_FILE + ' is missing!'
    quit()

beast_stream = open(DATA_FILE,'r')
text = beast_stream.read()
beast_stream.close()
info_lists = text.split('*') # splits into multiple lists of items

## build information lists

fishes_raw = [f.strip() for f in info_lists[0].split('~') if f.strip()]
bugs_raw = [b.strip() for b in info_lists[1].split('~') if b.strip()]
events_raw = None
if len(info_lists) > 2:
    events_raw = [l.strip() for l in info_lists[2].split('~') if l.strip()]
fishes = []
bugs = []

###
# Each animal entry is a dictionary in the format of:
# {'name'       : 'ANIMAL NAME',
#  'price'      : INTEGER_PRICE,
#  'loc'        : 'LOCATION',
#  'months'     : [MONTH1, MONTH2, ... , MONTHn],
#  'hours'      : [HOUR1, HOUR2, ... , HOURn],
#  'hours_raw'  : 'HUMAN-READABLE HOURS'}
#  'months_raw' : 'HUMAN-READABLE MONTHS'}
#  'caught'     : True if in caught database, False if not
###

## make fish list
for raw_fish in fishes_raw:
    new_fish = {}
    raw_info = raw_fish.split('\n')
    new_fish['name'] = raw_info[0].replace('Name: ','')
    new_fish['price'] = int_comma(raw_info[1].split()[1])
    new_fish['loc'] = raw_info[2].replace('Location: ','')

    new_fish['hours_raw'] = raw_info[4].replace('Time: ','')
    new_fish['months_raw'] = raw_info[3].replace('Month: ','')

    hourspan_raw = new_fish['hours_raw'].split(' - ')
    monthspan_raw = new_fish['months_raw'].split(' - ')

    new_fish['months'] = expand_months(monthspan_raw)
    new_fish['hours'] = expand_hours(hourspan_raw)

    new_fish['caught'] = False

    fishes.append(new_fish)

## make bug list
for raw_bug in bugs_raw:
    new_bug = {}
    raw_info = raw_bug.split('\n')
    new_bug['name'] = raw_info[0].replace('Name: ','')
    new_bug['price'] = int_comma(raw_info[1].split()[1])
    new_bug['loc'] = raw_info[2].replace('Location: ','')

    new_bug['hours_raw'] = raw_info[4].replace('Time: ','')
    new_bug['months_raw'] = raw_info[3].replace('Month: ','')

    hourspan_raw = new_bug['hours_raw'].split(' - ')
    monthspan_raw = new_bug['months_raw'].split(' - ')

    new_bug['months'] = expand_months(monthspan_raw)
    new_bug['hours'] = expand_hours(hourspan_raw)

    new_bug['caught'] = False

    bugs.append(new_bug)

## read from/create catch file and update fish/bug lists
catch_file = open(CATCH_FILE,'rw')
caught_list = catch_file.readlines()
caught_list = [n.strip() for n in caught_list if n.strip()] # cleaning

fish_names = [c['name'] for c in fishes]
bug_names = [c['name'] for c in bugs]
for a in caught_list:
    if a in fish_names:
        idex = fish_names.index(a)
        fishes[idex]['caught'] = True
    elif a in bug_names:
        idex = bug_names.index(a)
        bugs[idex]['caught'] = True

catch_file.close()

## set initial display variables
# initially show fishes sorted by location
sort_by = 'loc'
display_list = fishes
animalname = 'Fish' # for display purposes; can be "Fish" or "Bugs"
lookup_animal = None # for individual animal info displaying
message = 'Welcome to Animal Crossing Clock!\n' # console-type message
message += 'Currently displaying fishes, sorted by location.'
events_mode = False

## show info display
while(True):
    os.system('clear') # clear the screen
    now_hour = time.strftime('%H') # string of hour ('0'-'23')
    now_month = time.strftime('%B') # English string of month (ex: 'December')

	# for percentage display
    caught_fish_count = float(len([f for f in fishes if f['caught']]))
    caught_bug_count = float(len([b for b in bugs if b['caught']]))
    fish_count = float(len(fishes))
    bug_count = float(len(bugs))
    fish_percent_str = str(int(float(caught_fish_count/fish_count)*100))
    bugs_percent_str = str(int(float(caught_bug_count/bug_count)*100))
    
    status = 'It is now '+str(int(now_hour))+"'o'clock in the month of "+now_month+". Fish: "+fish_percent_str+"% Bugs: "+bugs_percent_str+"%"

    # build lists of animals catchable now/this month
    animal_now_catchable = []
    animal_month_catchable = []
    for animal in display_list:
        if now_month in animal['months'] and int(now_hour) in animal['hours']:
            animal_now_catchable.append(animal)
        elif now_month in animal['months']:
            animal_month_catchable.append(animal)

    # sorting list of dicts by val; thanks Stack Overflow and Google
    animal_now_catchable = sorted(animal_now_catchable, key=lambda k: k[sort_by])
    animal_month_catchable = sorted(animal_month_catchable, key=lambda k: k[sort_by])

    print animalname+' you can catch now ('+str(len(animal_now_catchable))+'):'
    for animal in animal_now_catchable:
        out_line = pretty_animal(animal)
        if animal['caught']:
            out_line = '[C] ' + out_line
        else:
            out_line = '[ ] ' + out_line
        print out_line

    # if there are fish catchable this month that aren't available right now
    if len(animal_month_catchable):
        print
        print 'Other '+animalname.lower()+' you can catch this month:'
        for animal in animal_month_catchable:
            out_line = pretty_animal(animal) + ' ::: at: '
            out_line += animal['hours_raw']
            if animal['caught']:
                out_line = '[C] ' + out_line
            else:
                out_line = '[ ] ' + out_line
            print out_line

    ## animal lookup mode
    if lookup_animal:
        os.system('clear')
        idex = [c['name'].lower() for c in fishes + bugs].index(lookup_animal.lower())
        display_animal = (fishes + bugs)[idex]
        print pretty_animal(display_animal)
        print 'Hours: '+display_animal['hours_raw']
        print 'Months: '+display_animal['months_raw']
        print 'Caught: '+{True : 'Yes', False : 'No'}[display_animal['caught']]
        print
        lookup_animal = None

    ### events mode
    ### XXX: This is a hidden feature right now. # actually it's not a feature at all
    #if events_mode and events_raw: # makes sure there is an events list to work with
        #os.system('clear')
        #events_mode = False
        #idex = MONTHS.index(now_month)
        #eventblock = events_raw[idex]
        #events = [e.strip() for e in eventblock.split('\n--') if e.strip()]
        #for i, e in enumerate(events): # remove the Tips section for each event
            #if 'Tips' in e:
                #idex = e.index('Tips')
                #events[i] = e[:idex]
        #print '================== EVENTS =================='
        #print
        #print '\n'.join(events)

    ## display options

    print
    print 'Options: "loc" sorts by location, "price" sorts by price, "name" sorts by name'
    print '         "bugs" to switch to bugs, "fish" to switch to fishes, "quit" to quit'
    print '         "[animal name]" for animal info, "[un]caught [animal name]" to mark an animal as un/caught'
    #print '         "events" for this month\'s events'
    print status
    print message
    filter = raw_input('Press enter to refresh or enter one of the options: ').lower().strip()

    # checks the input
    if filter in animal_now_catchable[0].keys():
        sort_by = filter
        message = 'Sorted by '+filter
    elif filter == 'bugs':
        display_list = bugs
        animalname = 'Bugs'
        message = 'Showing bugs'# ('+str(len(bugs))+')'
    elif filter == 'fish' or filter == 'fishes':
        display_list = fishes
        animalname = 'Fish'
        message = 'Showing fishes'# ('+str(len(fishes))+')'
    elif filter in [c['name'].lower() for c in fishes + bugs]:
        lookup_animal = filter
        message = 'Displaying animal information'
    #elif filter == 'events':
        #events_mode = True
        #message = 'Displaying events for this month; scroll up to see all'
    elif filter.startswith('caught '):
        caught_animal_name = filter.replace('caught ','')
        if caught_animal_name in [c['name'].lower() for c in fishes + bugs]:
            idex = [c['name'].lower() for c in fishes + bugs].index(caught_animal_name.lower())
            caught_animal = (fishes + bugs)[idex]
            if not caught_animal['caught']:
                caught_animal['caught'] = True
                catch_file = open(CATCH_FILE,'a')
                catch_file.write(caught_animal['name']+'\n')
                catch_file.close()
                message = 'Marked '+caught_animal['name']+' as caught'
            else:
                message = 'That animal is already caught, dood'
        else:
            message = 'That\'s not a name of a known animal'
    elif filter.startswith('uncatch '):
		uncaught_animal_name = filter.replace('uncatch ','')
		if uncaught_animal_name in [c['name'].lower() for c in fishes + bugs]:
			idex = [c['name'].lower() for c in fishes + bugs].index(uncaught_animal_name.lower())
			uncaught_animal = (fishes + bugs)[idex]
			if uncaught_animal['caught']:
				uncaught_animal['caught'] = False
				catch_file = open(CATCH_FILE,'r')
				caught_animal_names = [l for l in catch_file.read().split('\n') if l.strip().lower() != uncaught_animal_name.lower()]
				catch_file.close()
				catch_file = open(CATCH_FILE,'w')
				catch_file.write('\n'.join(caught_animal_names))
				catch_file.close()
				message = 'Unmarked '+uncaught_animal['name']+' as caught'
			else:
				message = 'That animal is hasn\'t been marked caught.'
		else:
			message = 'That\'s not a name of a known animal'
    elif filter == 'quit':
        os.system('clear')
        quit()
    elif filter:
        message = 'Invalid command.. (for reference: it\'s spelled "Coelacanth")'
        if '"' in filter:
            message = 'Don\'t include the quotation marks..'
    else: message = ''
